"""
Common functions related to the menu.


Since the menu doens't have views, then the queries and some common
functions are setted here.
"""
from menu.models import MenuEntry
from django.db.models.query import Q

from django.core.cache import cache

def get_user_tree_menu(user):
    """ Searches the menu for the user.
    There a few things to take into account:
        - the menu for the logged in users will always be the same
    (ie, they shouldn't use groups). The same forthe superuser and
    anonymous.
        - if the user is part of the staff then it will save the
    menu in the cache using the menu id.

    :returns:
        a tree structure of the menu. Each of the MenuEntry's will have
        and additional attribute that is children, that are the submenues.
    """

    cache_name = 'anonymus_menu'
    user_type = 'A'
    groups = []
    if user is not None and not user.is_anonymous():
        if user.is_superuser:
            cache_name = 'superuser_menu'
            user_type = 'X'
        elif user.has_perm('menu.logged_in'):
            cache_name = 'loggedin_menu'
            user_type = 'L'
        elif user.has_perm('menu.staff'):
            user_type = 'S'
            cache_name = 'staff_menu_%s' % user.id
            groups = user.groups()

    data = cache.get(cache_name)
    if not data:
        data = get_general_menu(user_type, groups)
        tree = convert_to_tree(data)
        cache.set(cache_name, tree) 
    return data
      
       

def get_general_menu(user_type, groups):
    """ Get the menu for the user type and the groups.
    The groups will only be used if the user type is 'Staff'.    
    """
    query_conditions = Q()
    if user_type == 'A':
        query_conditions = Q(user_type__exact = 'A') & \
                           Q(groups__isnull=True)
    elif user_type == 'S':
        query_conditions = (Q(user_type__exact = 'A') | \
               Q(user_type__exact = 'L') | \
               Q(user_type__exact = 'S') \
               & ( \
                 Q(groups__in=groups) | Q(groups__isnull=True)))
    else:
        query_conditions = (Q(user_type__exact = 'A') | \
               Q(user_type__exact = 'L')) \
               & ( \
                    Q(groups__in=groups) | Q(groups__isnull=True))
    

    menu_entries = MenuEntry.objects.filter(query_conditions).all()
    return menu_entries


def convert_to_tree(data):
    """ Convert the result of the query into a tree structure.
    
    :parameters:
        data: 
            a list of of MenuObjects.
    
    :returns:
        a list of MenuObjects, that are modified. Each MenuObject
        has an aditional attribute ``children`` that are the
        MenuObjects that depends of that options.
    """
    # TODO: ver si hay alguno metodo mas optimo
    tree = []
    
    # Search for the main options, ie, all the entries that don't have
    # any parent.
    
    for entry in data:
        if entry.parent is None:
            tree.append(entry)

    for entry in tree:
        create_sub_tree(entry, data)
       
def create_sub_tree(menu_entry, data):
    """ Creates the tree using the menu_entry as the root of the tree.

    :parameters:
        menu_entry: MenuEntry
            the menu_entry that will be the parent of the tree.
        data: list(MenuEntry)
            the list of all the menu entries.
    """
    menu_entry.children = search_childs_of(menu_entry, data)
    for current_parent in menu_entry.children:
        current_parent.url = menu_entry.url + current_parent.url
        create_sub_tree(current_parent, data)
    
def search_childs_of(menu_entry, data):
    """ Search the childs of the menu_entry.
    
    It will return an empty list of the menu entry doesn't have
    any other menu entry that has ``menu_entry`` as a parent.

    :parameters:
        data: list(MenuEntry)
            all the menu entries.
        menu_entry: MenuEntry
            the entry to which search all the childs.
    
    :returns:
        a list with all the childs.
    """
    children = []
    for entry in data:
        if entry.parent and entry.parent.id == menu_entry.id:
            children.append(entry)
    return children


